

#include "Disk.h"


Disk::Disk(){
	timer = new Timer();
	sqlite3_open("data.db", &diskDB);
}

Disk::~Disk(){

	char* err = NULL;
	for (boost::unordered_map<int, boost::unordered_map<std::string, ValType > >::iterator it = rapresentationMap.begin(); it != rapresentationMap.end(); it++){
		std::string dbTableName = util::intToString(it->first);
		std::string query = "DROP TABLE ";
		query += "table" + dbTableName;
		sqlite3_exec(diskDB, query.c_str(), NULL, NULL, &err);
	}
	sqlite3_close(diskDB);
}


bool Disk::insert(PubPkt *pkt, key_t k){
	timer->start();

	char* err = NULL;
	char* errCreate = NULL;
	std::string query;
	std::string queryCreate;

	boost::unordered_map<int, boost::unordered_map<string, ValType > >::iterator it = rapresentationMap.find(pkt->getEventType());
	if (it != rapresentationMap.end()){
		cout << "Memory: Disk: start query creation..." << endl;
		std::string dbTableName = util::intToString(it->first);
		std::string query = "INSERT INTO ";
		query += "table" + dbTableName;
		query += " VALUES(";
		query += util::intToString(k);
		query += ", ";
		for (int i=0; i<pkt->getAttributesNum(); i++){
			Attribute attribute = pkt->getAttribute(i);
			if (attribute.type == INT) {
				query += util::intToString(attribute.intVal);
				query += ", ";
			}
			if (attribute.type == FLOAT) {
				query += util::floatToString(attribute.floatVal);
				query += ", "; }
			if (attribute.type == BOOL) {
				query += util::boolToString(attribute.boolVal);
				query += ", "; }
			if (attribute.type == STRING) {
				query += "'";
				query += attribute.stringVal;
				query += "'";
				query += ", ";
			}
		}
		query += util::longToString(pkt->getTimeStamp().getTimeVal()) + ")";
		//cout << query << endl;
		cout << "Memory: Disk: query created." << endl;
		sqlite3_exec(diskDB, query.c_str(), NULL, NULL, &err);
		cout << "Memory: Disk: query executed." << endl;
	} else {
		cout << "Memory: Disk: start table creation..." << endl;
		cout << "Memory: Disk: start query creation..." << endl;
		std::string dbTableName = util::intToString(pkt->getEventType());
		queryCreate = "CREATE TABLE ";
		queryCreate += "table" + dbTableName;
		queryCreate += "(key INT, "; // PRIMARY KEY
		query = "INSERT INTO ";
		query += "table" + dbTableName;
		query += " VALUES(";
		query += util::intToString(k);
		query += ", ";
		boost::unordered_map<string, ValType > attributesMap;
		for (int i=0; i<pkt->getAttributesNum(); i++){
			Attribute attribute = pkt->getAttribute(i);
			if (attribute.type == INT) {
				query += util::intToString(attribute.intVal) + ", ";
				queryCreate += attribute.name;
				queryCreate += " INT, ";
				attributesMap.insert(make_pair(attribute.name, INT));
			}
			if (attribute.type == FLOAT) {
				query += util::floatToString(attribute.floatVal);
				query += ", ";
				queryCreate += attribute.name;
				queryCreate += " FLOAT, ";
				attributesMap.insert(make_pair(attribute.name, FLOAT));
			}
			if (attribute.type == BOOL){
				query += util::boolToString(attribute.boolVal);
				query += ", ";
				queryCreate += attribute.name;
				queryCreate += " BOOLEAN, ";
				attributesMap.insert(make_pair(attribute.name, BOOL));
			}
			if (attribute.type == STRING){
				query += "'";
				query += attribute.stringVal;
				query += "'";
				query += ", ";
				queryCreate += attribute.name;
				queryCreate += " VARCHAR(";
				queryCreate += util::intToString(STRING_VAL_LEN);
				queryCreate += "), ";
				attributesMap.insert(make_pair(attribute.name, STRING));
			}
		}
		query += util::longToString(pkt->getTimeStamp().getTimeVal()) + ")";
		queryCreate += "timestamp UNSIGNED BIG INT)";
		rapresentationMap.insert(make_pair(pkt->getEventType(), attributesMap));
		cout << queryCreate << endl;
		cout << query << endl;
		cout << "Memory: Disk: query created." << endl;
		sqlite3_exec(diskDB, queryCreate.c_str(), NULL, NULL, &errCreate);
		cout << "Memory: Disk: table created." << endl;
		sqlite3_exec(diskDB, query.c_str(), NULL, NULL, &err);
		cout << "Memory: Disk: query executed." << endl;
	}

	keyTypeMatchingMap.insert(make_pair(k, pkt->getEventType()));
	if (err != NULL || errCreate != NULL) {
		cout << "Memory: Disk: insertion failed: " << err << endl;
		return false;
	}

	timer->stop();
	cout << "Memory: Disk: insertion terminated in " << timer->getElapsedTimeInMilliSec() << " ms." << endl;
	return true;
}


bool Disk::remove(std::pair<timestamp_t, key_t> k){
	timer->start();

	char* err = NULL;
	std::string query;

	boost::unordered_map<key_t, int>::iterator it = keyTypeMatchingMap.find(k.second);
	if (it != keyTypeMatchingMap.end()){
		cout << "Memory: Disk: start query creation..." << endl;
		std::string dbTableName = util::intToString(it->second);
		query = "DELETE FROM ";
		query += "table" + dbTableName;
		query += " WHERE key=";
		query += util::intToString(k.second);
		cout << query << endl;
		cout << "Memory: Disk: query created." << endl;
		sqlite3_exec(diskDB, query.c_str(), NULL, NULL, &err);
		cout << "Memory: Disk: query executed." << endl;
		if (err != NULL){
			cout << "Memory: Disk: remotion failed: " << err << endl;
			return false;
		}
		keyTypeMatchingMap.erase(it);
		timer->stop();
		cout << "Memory: Disk: remotion terminated in " << timer->getElapsedTimeInMilliSec() << " ms." << endl;
		return true;
	}
	cout << "Memory: Disk: remotion failed: message not found." << endl;
	return false;
}


void Disk::extract(int type, Constraint* constraint, int numConstraint, timestamp_t entryTimestamp, float window, OccurrenceType occurrence, std::set<PubPkt*> &resultSet){

	timer->start();

	char* err = NULL;
	std::string query;
	std::string antequery;
	std::string middlequery;
	std::string postquery;

	DiskParamType* param = (DiskParamType*)malloc(sizeof(DiskParamType));
	param->resultSet = &resultSet;
	boost::unordered_map<int, boost::unordered_map<string, ValType > >::iterator it = rapresentationMap.find(type);
	if (it == rapresentationMap.end()){
		timer->stop();
		cout << "Memory: Disk: extraction terminated in " << timer->getElapsedTimeInMilliSec() << " ms." << endl;
		return;
	}
	param->itAttributeMap = it;

	cout << "Memory: Disk: start query creation..." << endl;
	antequery = "SELECT * FROM ";
	middlequery = "table" + util::intToString(type);
	middlequery += " WHERE ";
	for (int i=0; i<numConstraint; i++){
		middlequery += constraint[i].name;
		if (constraint[i].op == EQ) middlequery += "=";
		if (constraint[i].op == LT) middlequery += "<=";
		if (constraint[i].op == GT) middlequery += ">=";
		if (constraint[i].type == INT) {
			middlequery += util::intToString(constraint[i].intVal);
			middlequery += " AND ";
		}
		if (constraint[i].type == FLOAT) {
			middlequery += util::floatToString(constraint[i].floatVal);
			middlequery += " AND ";
		}
		if (constraint[i].type == BOOL) {
			middlequery += util::boolToString(constraint[i].boolVal);
			middlequery += " AND ";
		}
		if (constraint[i].type == STRING) {
			middlequery += "'";
			middlequery += constraint[i].stringVal;
			middlequery += "'";
			middlequery += " AND ";
		}
	}
	postquery = "timestamp<=";
	postquery += util::longToString(entryTimestamp);
	postquery += " AND timestamp>=";
	postquery += util::longToString(entryTimestamp - (int long)window);

	if (occurrence == LAST) {
		query = antequery + middlequery + "timestamp = (SELECT max(timestamp) FROM " + middlequery + postquery + ")";
	} else {
		query = antequery + middlequery + postquery + " ORDER BY timestamp";
	}
	cout << query << endl;
	cout << "Memory: Disk: query created." << endl;
	sqlite3_exec(diskDB, query.c_str(), disk::addToResultSet, (void*)param, &err);
	cout << "Memory: Disk: query executed." << endl;
	timer->stop();

	cout << "Memory: Disk: extraction terminated in " << timer->getElapsedTimeInMilliSec() << " ms." << endl;
}


int disk::addToResultSet(void* input, int argc, char **argv, char **column){

	DiskParamType* param = (DiskParamType*)input;

	int numAttributes = argc-2;
	Attribute attributes[numAttributes];
	for (int i=0; i<numAttributes; i++){
		sprintf(attributes[i].name, "%s", column[i+1]);
		//attributes[i].type = param->attributeMap[column[i+1]];
		attributes[i].type = param->itAttributeMap->second[column[i+1]];
		if (attributes[i].type == INT) attributes[i].intVal = atoi(argv[i+1]);
		if (attributes[i].type == FLOAT) attributes[i].floatVal = atof(argv[i+1]);
		if (attributes[i].type == BOOL) { if (atoi(argv[i+1])==1) attributes[i].boolVal = true; else attributes[i].boolVal = false; }
		if (attributes[i].type == STRING) sprintf(attributes[i].stringVal, "%s", argv[i+1]);
	}
	PubPkt *pkt = new PubPkt(param->itAttributeMap->first, attributes, numAttributes);
	pkt->incRefCount();
	param->resultSet->insert(pkt);
	return 0;
}





